#include <iostream>
#include <sstream>
#include <string>

#include "predictor_base.h"
#include "predictor_link.h"

using namespace std;

Predictor_base::Predictor_base(string id)
{
  //cout << "  Creating predictor_base " << id << endl;
  set_string_id(id);

  // add output link
  Predictor_link *l = new Predictor_link(OUTPUT);
  l->set_source(this);
  set_output_link(l);

  // reset internal state
  _active = false;
  _error = 0.0;
  _saved_time = 0.0;
}

void Predictor_base::set_string_id(string id)
{
  _string_id = id;
}

std::string Predictor_base::get_string_id(void)
{
  return _string_id;
}

std::string Predictor_base::get_string_type(void)
{
  return _string_type;
}

Predictor_base::~Predictor_base()
{
}

void Predictor_base::reset(void)
{
  _active = false;
  _error = 0.0;
  _saved_time = 0.0;

  _output_link->reset();
}

void Predictor_base::set_output_link(Predictor_link *l)
{
  _output_link = l;
  l -> set_source(this);
}

void Predictor_base::add_input_link(Predictor_link *l)
{
  //cout << "Adding input link " << l << endl;
  _input_links.push_back(l);
  l -> add_target(this);
  _saved_inputs.resize(_input_links.size());
}

void Predictor_base::save_input(float time)
{
  _saved_time = time;
  int n = _input_links.size();
  for (int i = 0; i < n; ++i) {
    _saved_inputs[i] = _input_links[i]->get_value();
  }
}

float Predictor_base::get_predicted_input(int i)
{
  if (i < (int) _predicted_inputs.size()) {
    return _predicted_inputs[i];
  }
  cout << "ERROR: Index out of range: " << i << endl;
  exit(1);
}

void Predictor_base::calculate_error(float t)
{
  _error = 0.0;
  int n = (int) _input_links.size();
  for (int i = 0; i < n; ++i) {
    if (_input_links[i]->get_link_type() == INPUT) {
      float t = _input_links[i]->get_value() - _predicted_inputs[i];
      _error += t * t;
      //      cout << i << " actual:" << _input_links[i]->get_value()
      //          << ", predicted: " << _predicted_inputs[i] << endl;
    }
  }
  //  cout << "  Error: " << _error << " " << t << " " << _saved_time << endl;
  _error /= (t - _saved_time) * (t - _saved_time);
  //  cout << "  Error: " << _error << endl;
}

void Predictor_base::activate(float critical_error)
{
  if (_error > critical_error) {
    _active = false;
    return;
  }
  _active = true;
}

float Predictor_base::get_error(void)
{
  return _error;
}

bool Predictor_base::is_active(void)
{
  return _active;
}

Predictor_link *Predictor_base::get_output_link(void)
{
  if (_output_link == NULL) {
    cout << "ERROR: output link is NULL" << endl;
    exit(1);
  }
  return _output_link;
}

void Predictor_base::set_parameter(double value)
{
  if(_input_links[0]->get_link_type() != PARAMETER) {
    cout << "ERROR: link 0 is not a parameter" << endl;
  }
  _input_links[0]->set_value(value);
}

Predictor_link *Predictor_base::get_input_link(int i)
{
  if (i >= (int) _input_links.size() || _input_links[i] == NULL) {
    cout << "ERROR: input link " << i << " is NULL" << endl;
    exit(1);
  }
  return _input_links[i];
}

float Predictor_base::get_complexity(void) const
{
  return _complexity;
}

void Predictor_base::info(stringstream &ss)
{
  ss << "Type: " << _string_type << endl;
  ss << "Error: " << _error << endl;
  if(_active) {
    ss << "Active" << endl;
  }
  else {
    ss << "In-active" << endl;
  }
}

